package com.feeyo.prophet.service;

import com.feeyo.prophet.mapper.InterfaceVisitsInfoMapper;
import com.feeyo.prophet.pojo.po.InterfaceVisitsInfo;
import com.google.common.collect.Lists;
import jakarta.annotation.PreDestroy;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.LongAdder;

/**
 * 接口访问统计
 *
 * @author caoying
 * @author jiangyehui
 * @date 2024/2/29
 * @copyright 2022 www.variflight.com Inc. All rights reserved
 */
@Slf4j
@Service
public class InterfaceStatisticsService {

    private static final ConcurrentHashMap<String, StatInfo> STAT_INFO_MAP = new ConcurrentHashMap<>();
    private static final String UN_KNOW = "unknow";
    private static volatile long version = System.currentTimeMillis();

    @Value(value = "${prophet.auth.productName}")
    private String productName;


    private String localHost;
    private final InterfaceVisitsInfoMapper interfaceVisitsInfoMapper;

    @Autowired
    public InterfaceStatisticsService(InterfaceVisitsInfoMapper interfaceVisitsInfoMapper) {
        this.interfaceVisitsInfoMapper = interfaceVisitsInfoMapper;
        try {
            this.localHost = InetAddress.getLocalHost().getHostAddress();
        } catch (UnknownHostException e) {
            log.error("get local host error", e);
        }
    }

    private static final String SPLIT = "^^";

    private static final ThreadLocal<Long> dataNumThreadLocal = ThreadLocal.withInitial(() -> 1L);

    public static void setDataNum(long dataNum) {
        dataNumThreadLocal.set(dataNum);
    }

//    @Scheduled(cron = "0/5 * * * * ?")
    public void startUp() {
        long preVersion = version;
        version = System.currentTimeMillis();
        List<InterfaceVisitsInfo> list = Lists.newArrayList();
        Iterator<Map.Entry<String, StatInfo>> iterator = STAT_INFO_MAP.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, StatInfo> next = iterator.next();
            String key = next.getKey();
            String[] split = key.split("\\^\\^");
            String currVersion = split[0];
            if (!StringUtils.equals(currVersion, String.valueOf(preVersion))) {
                continue;
            }
            StatInfo statInfo = next.getValue();
            InterfaceVisitsInfo info = convert(split, statInfo);
            list.add(info);
            iterator.remove();
        }
        if (!list.isEmpty()) {
            interfaceVisitsInfoMapper.insertList(list);
        }
    }

    private InterfaceVisitsInfo convert(String[] split, StatInfo statInfo) {
        InterfaceVisitsInfo info = new InterfaceVisitsInfo();
        String appId = split[1];
        info.setAppId(appId);
        info.setProjectName(productName);
        String uri = split[3];
        info.setRequestUrl(uri);
        info.setRequestNum((int) statInfo.getCount());
        info.setDataNum((int) statInfo.getRecordCount());
        info.setServerHost(localHost);
        String clientIp = split[2];
        info.setClientHost(clientIp);
        info.setAddTime((int) (statInfo.getAddTime() / 1000));
        info.setUpdateTime((int) (statInfo.getUpdateTime() / 1000));
        return info;
    }

    /**
     * 统计前置方法
     *
     * @param appId
     * @param uri
     * @param clientIp
     * @return
     */
    public String before(String appId, String uri, String clientIp) {
        try {
            if (StringUtils.isEmpty(appId)) {
                appId = UN_KNOW;
            }
            String key = version + SPLIT + appId + SPLIT + clientIp + SPLIT + uri;
            StatInfo statInfo = STAT_INFO_MAP.computeIfAbsent(key, o -> new StatInfo());
            statInfo.countIncr();
            return key;
        } catch (Exception e) {
            log.error("#before err:", e);
            return null;
        }
    }

    /**
     * 统计后置方法
     *
     * @param key
     */
    public void after(String key) {
        try {
            StatInfo statInfo = STAT_INFO_MAP.get(key);
            if (null == statInfo) {
                return;
            }
            statInfo.setUpdateTime(System.currentTimeMillis());
            Long dataNum = dataNumThreadLocal.get();
            statInfo.recordCountAdd(dataNum);
        } catch (Exception e) {
            log.error("#affer err:", e);
        } finally {
            dataNumThreadLocal.remove();
        }
    }

    /**
     * 统计详情
     *
     * @author caoying
     * @author jiangyehui
     * @date 2022/10/9
     * @copyright 2022 www.variflight.com Inc. All rights reserved
     */
    public static class StatInfo {

        /**
         * 添加时间
         */
        private long addTime;

        /**
         * 更新时间
         */
        private long updateTime;

        /**
         * 请求次数
         */
        private final LongAdder count = new LongAdder();

        /**
         * 响应结果数量
         */
        private final LongAdder recordCount = new LongAdder();

        public StatInfo() {
            long now = System.currentTimeMillis();
            this.addTime = now;
            this.updateTime = now;
        }

        public long getAddTime() {
            return addTime;
        }

        public void setAddTime(long addTime) {
            this.addTime = addTime;
        }

        public long getUpdateTime() {
            return updateTime;
        }

        public void setUpdateTime(long updateTime) {
            this.updateTime = updateTime;
        }

        public void countIncr() {
            count.increment();
        }

        public long getCount() {
            return count.sum();
        }

        public void recordCountAdd(long num) {
            recordCount.add(num);
        }


        public long getRecordCount() {
            return recordCount.sum();
        }

        @Override
        public String toString() {
            final StringBuilder str = new StringBuilder("{");
            str.append("\"addTime\":")
                    .append(addTime);
            str.append(",\"updateTime\":")
                    .append(updateTime);
            str.append(",\"count\":")
                    .append(count);
            str.append(",\"recordCount\":")
                    .append(recordCount);
            str.append('}');
            return str.toString();
        }
    }

    @PreDestroy
    public void stop() {
        List<InterfaceVisitsInfo> list = Lists.newArrayList();
        log.info("do statistics stop");
        Iterator<Map.Entry<String, StatInfo>> iterator = STAT_INFO_MAP.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, StatInfo> next = iterator.next();
            String key = next.getKey();
            String[] split = key.split("\\^\\^");
            StatInfo statInfo = next.getValue();
            InterfaceVisitsInfo info = convert(split, statInfo);
            list.add(info);
            iterator.remove();
        }
        if (!list.isEmpty()) {
            log.info("last finish stat size:{}", list.size());
            interfaceVisitsInfoMapper.insertList(list);
        }
    }


}
